package br.ufmg.dees.insane.parser;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.Set;

import br.ufmg.dees.insane.model.femModel.FemModel;
import br.ufmg.dees.insane.model.femModel.element.Element;
import br.ufmg.dees.insane.model.femModel.elementNode.ElementNode;
import br.ufmg.dees.insane.model.femModel.node.Node;
import br.ufmg.dees.insane.model.postp.Boundary;
import br.ufmg.dees.insane.model.postp.GeoPostpModel;
import br.ufmg.dees.insane.model.postp.Patch;
import br.ufmg.dees.insane.model.postp.PointModel;
import br.ufmg.dees.insane.shape.Q9;
import br.ufmg.dees.insane.shape.Q9H;
import br.ufmg.dees.insane.shape.RMCIQ9;
import br.ufmg.dees.insane.shape.Shape;
import br.ufmg.dees.insane.shape.T10;
import br.ufmg.dees.insane.solution.Solution;
import br.ufmg.dees.insane.util.IPoint3d;
import br.ufmg.dees.insane.util.IVector;
import br.ufmg.dees.insane.util.pointValues.PointValues;
import br.ufmg.dees.insane.util.pointValues.StringPointValues;

/**
 * This class fill a PostpModel from a FemModel.
 * @author Samuel Silva Penna
 * @since 16/06/2006
 */
public class NodalFemToGeoPostpModel implements  ParserPostpModel {
	
	//observer of the FemModel...
	// is observed by parserHalfEdge...
	
	private FemModel fem;
	private Solution solution;
	private GeoPostpModel geoPostpModel;
	private double loadFactor = 0.0;
	
	/** The constructor
	 * Constructs a PostpModel from a FemModel.
	 * @param fem The fem model to be passed as parameter
	 */
	public NodalFemToGeoPostpModel(FemModel fem){
		this.fem = fem;
		this.fem.initKeys();
		this.fillPostpModel();
	}
	
	/** The constructor
	 * Constructs a PostpModel from a FemModel.
	 * @param fem The fem model to be passed as parameter
	 */
	public NodalFemToGeoPostpModel(FemModel fem, Solution solution){
		this.fem = fem;
		this.fem.initKeys();
		this.solution = solution;
		this.fillPostpModel();
	}
	
	public void fillPostpModel(){
		ArrayList<PointModel> points = new ArrayList<PointModel>();
		ArrayList<Patch> patches = new ArrayList<Patch>();
		ArrayList<Boundary> boundaries  = new ArrayList<Boundary>();
		ArrayList elements = this.fem.getElementsList();
		ArrayList nodes = this.fem.getNodesList();
		PointModel pointModelTemp;
		for (int i = 0; i < nodes.size(); i++){
			Node n = (Node)nodes.get(i);
			pointModelTemp = new PointModel(new IPoint3d(n.getPoint().getCoords()));
			pointModelTemp.setLabel(n.getLabel());
			points.add(pointModelTemp);
		}
		
		Patch patch;
		PointModel pointModel;
		String[] patchLabels;
		//Setting the nodal values into a PointModel
		//covering the element list
		for(int i=0;i<elements.size();i++){
			Shape shape = ((Element)this.fem.getElementsList().get(i)).getShape();
			HashMap<String,Object> patchValues = new HashMap<String,Object>();
			//This if test the shape of element. The shapes Q9, Q9h, T10 has diferent treat
			if(shape instanceof Q9 || shape instanceof Q9H ||shape instanceof RMCIQ9 || shape instanceof T10){
				patchLabels = new String[((Element)elements.get(i)).getIncidence().size()-1];
				for(int j=0;j<((Element)elements.get(i)).getIncidence().size()-1;j++){
					ElementNode node = ((Element)elements.get(i)).getNode(j);
					Node n = node.getNode();
					pointModel = returnPointModel(n.getLabel(),points);
					Set keys = n.getNodeValues().keySet();
					Iterator iter = keys.iterator();
					StringPointValues doflabels = (StringPointValues)n.getNodeValues(n.DOF_LABELS);
					if(doflabels!=null){
						while (iter.hasNext()) {
							String key = (String) iter.next();
							PointValues pv = n.getNodeValues(key);
							//fill the degrees of freedom of this model
							for (int k = 0; k < doflabels.getSize(); k++) {
								if(!key.equals("DOF_LABELS")){
									if(pv.getPointValue(k)!=null){
										pointModel.putValue(key+"-"+doflabels.getStringPointValue(k),pv.getPointValue(k));
									}
								}
								
							} 
						}
						
						//values respective to internal variable and dual variable
						IVector intvar = new IVector(((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels().length);
						((Element)elements.get(i)).getNodalPointInternalVariables().getColumn(j,intvar);
						IVector dualIntvar = new IVector(((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels().length);
						((Element)elements.get(i)).getNodalPointDualInternalVariables().getColumn(j,dualIntvar);
						
						IVector intvarExtra = new IVector(((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels().length);
						((Element)elements.get(i)).getExtrapolatedNodalPointInternalVariables().getColumn(j,intvarExtra);
						IVector dualIntvarExtra = new IVector(((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels().length);
						((Element)elements.get(i)).getExtrapolatedNodalPointDualInternalVariables().getColumn(j,dualIntvarExtra);
						
						for(int m=0;m<((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels().length;m++){
							String keyS = ((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels()[m];
							if(pointModel.getValues().containsKey(keyS)){
								Integer cont = ((Integer)pointModel.getCont(keyS)+1);
								pointModel.putCont(keyS,cont);
								double value = ((Double)pointModel.getValue(keyS)+(Double)intvar.getElement(m));
								pointModel.putValue(keyS,value);
							}else{
								Integer cont = 1;
								pointModel.putCont(keyS,cont);
								double value = intvar.getElement(m);
								pointModel.putValue(keyS,value);
							}
							String keyT = ((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels()[m];
							if(pointModel.getValues().containsKey(keyT)){
								Integer cont = ((Integer)pointModel.getCont(keyT)+1);
								pointModel.putCont(keyT,cont);
								double value = ((Double)pointModel.getValue(keyT)+dualIntvar.getElement(m));
								pointModel.putValue(keyT,value);
							}else{
								Integer cont = 1;
								pointModel.putCont(keyT,cont);
								
								double value = dualIntvar.getElement(m);
								pointModel.putValue(keyT,value);
							}
//							
//							String keySE = ((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels()[m]+"-Extra";
//							if(pointModel.getValues().containsKey(keySE)){
//								Integer cont = ((Integer)pointModel.getCont(keySE)+1);
//								pointModel.putCont(keySE,cont);
//								double value = ((Double)pointModel.getValue(keySE)+intvarExtra.getElement(m));
//								pointModel.putValue(keySE,value);
//							}else{
//								Integer cont = 1;
//								pointModel.putCont(keySE,cont);
//								double value = intvarExtra.getElement(m);
//								pointModel.putValue(keySE,value);
//							}
//							
//							String keyTE = ((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels()[m]+"-Extra";
//							if(pointModel.getValues().containsKey(keyTE)){
//								Integer cont = ((Integer)pointModel.getCont(keyTE)+1);
//								pointModel.putCont(keyTE,cont);
//								double value = ((Double)pointModel.getValue(keyTE)+dualIntvarExtra.getElement(m));
//								pointModel.putValue(keyTE,value);
//							}else{
//								Integer cont = 1;
//								pointModel.putCont(keyTE,cont);
//								double value = dualIntvarExtra.getElement(m);
//								pointModel.putValue(keyTE,value);
//							}
							
							patchValues.put(keyS,((Element)elements.get(i)).getNodalPointInternalVariables().getRow(m));
							patchValues.put(keyT,((Element)elements.get(i)).getNodalPointDualInternalVariables().getRow(m));
							//patchValues.put(keySE,((Element)elements.get(i)).getExtrapolatedNodalPointInternalVariables().getRow(m));
							//patchValues.put(keyTE,((Element)elements.get(i)).getExtrapolatedNodalPointDualInternalVariables().getRow(m));
							
						}//end of internal variables values

					}
					patchLabels[j] = pointModel.getLabel();
					
				}
				patch = new Patch(patchLabels);
				patch.setLabel(((Element)elements.get(i)).getLabel());
				ArrayList<String> incidence = new ArrayList<String>();
				for(int j=0;j<((Element)elements.get(i)).getIncidence().size();j++){
					incidence.add(((Element)elements.get(i)).getNode(j).getLabel());
				}
				patchValues.put("Incidence",incidence);
				patch.setValues(patchValues);
				patches.add(patch);
			}else{
				patchLabels = new String[((Element)elements.get(i)).getIncidence().size()];
				for(int j=0;j<((Element)elements.get(i)).getIncidence().size();j++){
					ElementNode node = ((Element)elements.get(i)).getNode(j);
					Node n = node.getNode();
					pointModel = returnPointModel(n.getLabel(),points);
					Set keys = n.getNodeValues().keySet();
					Iterator iter = keys.iterator();
					StringPointValues doflabels = (StringPointValues)n.getNodeValues(n.DOF_LABELS);
					if(doflabels!=null){
						while (iter.hasNext()) {
							String key = (String) iter.next();
							PointValues pv = n.getNodeValues(key);
							for (int k = 0; k < doflabels.getSize(); k++) {
								if(!key.equals("DOF_LABELS")){
									if(pv.getPointValue(k)!=null){
										pointModel.putValue(key+"-"+doflabels.getStringPointValue(k),pv.getPointValue(k));
									}
								}
							} 
						}
						
						//values respective to internal variable and dual variable
						IVector intvar = new IVector(((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels().length);
						((Element)elements.get(i)).getNodalPointInternalVariables().getColumn(j,intvar);
						IVector dualIntvar = new IVector(((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels().length);
						((Element)elements.get(i)).getNodalPointDualInternalVariables().getColumn(j,dualIntvar);
						
						IVector intvarExtra = new IVector(((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels().length);
						((Element)elements.get(i)).getExtrapolatedNodalPointInternalVariables().getColumn(j,intvarExtra);
						IVector dualIntvarExtra = new IVector(((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels().length);
						((Element)elements.get(i)).getExtrapolatedNodalPointDualInternalVariables().getColumn(j,dualIntvarExtra);
						
						for(int m=0;m<((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels().length;m++){
							String keyS = ((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels()[m];
							
							if(pointModel.getValues().containsKey(keyS)){
								Integer cont = ((Integer)pointModel.getCont(keyS)+1);
								pointModel.putCont(keyS,cont);
								
								double value = ((Double)pointModel.getValue(keyS)+intvar.getElement(m));
								pointModel.putValue(keyS,value);
							}else{
								Integer cont = 1;
								pointModel.putCont(keyS,cont);
								
								double value = intvar.getElement(m);
								pointModel.putValue(keyS,new Double(value));
							}
							
							String keyT = ((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels()[m];
							if(pointModel.getValues().containsKey(keyT)){
								Integer cont = ((Integer)pointModel.getCont(keyT)+1);
								pointModel.putCont(keyT,cont);
								double value = ((Double)pointModel.getValue(keyT)+dualIntvar.getElement(m));
								pointModel.putValue(keyT,value);
							}else{
								Integer cont = 1;
								pointModel.putCont(keyT,cont);
								
								double value = dualIntvar.getElement(m);
								pointModel.putValue(keyT,value);
							}
//							
//							String keySE = ((Element)elements.get(i)).getAnalysisModel().getInternalVariablesLabels()[m]+"-Extra";
//							if(pointModel.getValues().containsKey(keySE)){
//								Integer cont = ((Integer)pointModel.getCont(keySE)+1);
//								pointModel.putCont(keySE,cont);
//								
//								double value = ((Double)pointModel.getValue(keySE)+intvarExtra.getElement(m));
//								pointModel.putValue(keySE,value);
//							}else{
//								Integer cont = 1;
//								pointModel.putCont(keySE,cont);
//								
//								double value = intvarExtra.getElement(m);
//								pointModel.putValue(keySE,value);
//							}
//							
//							String keyTE = ((Element)elements.get(i)).getAnalysisModel().getDualInternalVariablesLabels()[m]+"-Extra";
//							if(pointModel.getValues().containsKey(keyTE)){
//								Integer cont = ((Integer)pointModel.getCont(keyTE)+1);
//								pointModel.putCont(keyTE,cont);
//								double value = ((Double)pointModel.getValue(keyTE)+dualIntvarExtra.getElement(m));
//								pointModel.putValue(keyTE,value);
//							}else{
//								Integer cont = 1;
//								pointModel.putCont(keyTE,cont);
//								double value = dualIntvarExtra.getElement(m);
//								pointModel.putValue(keyTE,value);
//							}
							
							patchValues.put(keyS,((Element)elements.get(i)).getNodalPointInternalVariables().getRow(m));
							patchValues.put(keyT,((Element)elements.get(i)).getNodalPointDualInternalVariables().getRow(m));
						//	patchValues.put(keySE,((Element)elements.get(i)).getExtrapolatedNodalPointInternalVariables().getRow(m));
						//	patchValues.put(keyTE,((Element)elements.get(i)).getExtrapolatedNodalPointDualInternalVariables().getRow(m));
							
						}//end of internal variables values
					}
					patchLabels[j] = pointModel.getLabel();
				}
				patch = new Patch(patchLabels);
				patch.setLabel(((Element)elements.get(i)).getLabel());
				ArrayList<String> incidence = new ArrayList<String>();
				for(int j=0;j<((Element)elements.get(i)).getIncidence().size();j++){
					incidence.add(((Element)elements.get(i)).getNode(j).getLabel());
				}
				patchValues.put("Incidence",incidence);
				patch.setValues(patchValues);
				patches.add(patch);
				
			}
		}//fim do preenchimento dos elementos
		ArrayList<PointModel> newPoints = setPointModel(points);
		this.geoPostpModel = new GeoPostpModel(newPoints,patches,boundaries);
		this.geoPostpModel.setLabel("NodalGeoPostp");
	}//end of this method
	
	/**The method check if a node exist in a list.
	 * @param vertice 
	 * @param verticesList
	 * @return compare
	 */
	@SuppressWarnings("unused")
	private boolean checkPointModel(PointModel point, ArrayList<PointModel> pointList){
		boolean compare = false;
		ListIterator itr = pointList.listIterator();	
		while(itr.hasNext()){
			PointModel p = (PointModel)itr.next();
			if(p.getLabel().equals(point.getLabel())){
				return true;
			}else{
				continue;
			}
		}
		return compare;
	}
	
	/**The method check if a node exist in a list.
	 * @param vertice 
	 * @param verticesList
	 * @return compare
	 */
	private PointModel returnPointModel(String label,ArrayList<PointModel> pointsList){
		ListIterator itr = pointsList.listIterator();    
		while(itr.hasNext()){
			PointModel p = (PointModel)itr.next();
			if(p.getLabel().equals(label)){
				return p;
			}else{
				continue;
			}
		}
		return null;
	}
	
	private ArrayList<PointModel> setPointModel(ArrayList<PointModel> pointsList){
		ArrayList<PointModel> points = new ArrayList<PointModel>();
		for (int i = 0; i < pointsList.size(); i++){
			PointModel temp = (PointModel)pointsList.get(i);
			Iterator itr = (temp.getContMap().keySet()).iterator();
			while (itr.hasNext()) {
				String key = (String)itr.next();
				double value = (Double)temp.getValue(key);
				Integer aux = (Integer)temp.getCont(key);
				double auxValue = value/aux;
				temp.putValue(key,new Double(auxValue));
			}
			points.add(temp);
		}            
		return points;
	}
	
	/** O metodo busca, na lista do modelo, um vertice igual ao vertice passado de parametro, e o retorna
	 * @param vertex
	 * @param verticesList
	 * @return
	 */
	public PointModel searchPointModel(PointModel point, ArrayList pointList){
		PointModel p = new PointModel();
		ListIterator itr = pointList.listIterator();	
		while(itr.hasNext()){
			p = (PointModel)itr.next();
			if(p.getLabel().equals(point.getLabel())){
				return  p;
			}else{
				continue;
			}
		}
		return null;
	}
	
	/**
	 * The method return the femModel.
	 * @return Returns The fem.
	 */
	public FemModel getFem() {
		return fem;
	}
	
	/**
	 * The method set the field fem from the parameter fem.
	 * @param fem The fem to set.
	 */
	public void setFem(FemModel fem) {
		this.fem = fem;
	}
	
	/**
	 * The method return the solution.
	 * @return Returns The solution.
	 */
	public Solution getSolution() {
		return solution;
	}
	
	/**
	 * The method set the field solution from the parameter solution.
	 * @param solution The solution to set.
	 */
	public void setSolution(Solution solution) {
		this.solution = solution;
	}
	
	public double getLoadFactor() {
		return loadFactor;
	}
	
	public void setLoadFactor(double loadFactor) {
		this.loadFactor = loadFactor;
	}
	
	public GeoPostpModel getGeoPostpModel() {
		return this.geoPostpModel;
	}
	
	public void setGeoPostpModel(GeoPostpModel model) {
		// TODO Auto-generated method stub
		
	}
	
//	***************************************************************************
}//end of this class

